import java.util.ArrayList;
import java.util.Random;

// the class create a game
public class Game implements Cloneable {
	// player's position, The position start from ZERO
	private ArrayList<Player> player;
	// the map size
	private int mapSizeX, mapSizeY;
	private Player[][] map;
	public Player controlPlayer;  // we need use it in BFS search

	public Game(int mapSizeX, int mapSizeY) {
		this.mapSizeX = mapSizeX;
		this.mapSizeY = mapSizeY;
		map = new Player[mapSizeX][mapSizeY];
		player = new ArrayList<Player>();
	}

	// add a player in the game, sign will show in console
	public Player addPlayer(char sign) {
		Player p = new Player(sign);
		player.add(p);
		return p;
	}
	
	// get the Game Status
	public GameStatus getGameStatus() {
		GameStatus res = new GameStatus();
		res.mapSizeX = this.mapSizeX;
		res.mapSizeY = this.mapSizeY;
		res.playerPosX = this.controlPlayer.posX;
		res.playerPosY = this.controlPlayer.posY;
		for (Player p : player) {
			if (p != controlPlayer) {
				res.enemyPosX = p.posX;
				res.enemyPosY = p.posY;
			}
		}
		res.direction = controlPlayer.getDirection();
		res.remainEnemy = player.size() - 1;
		return res;
	}

	// let a player attack
	public int attack(Player p) {
		Player temp = new Player();
		temp.posX = p.posX;
		temp.posY = p.posY;
		try {
			temp.setDirection(p.getDirection());
		} catch (Exception e) {
			e.printStackTrace();
		}
		int count = 0;
		temp.moveFront();
		if (temp.posX >= 0 && temp.posX < mapSizeX && temp.posY >= 0 && temp.posY < mapSizeY) {
			if (map[temp.posX][temp.posY] != null) {
				//System.out.println("you kill a enemy");
				count++;
				map[temp.posX][temp.posY] = null;
			}
		}
		temp.moveFront();
		if (temp.posX >= 0 && temp.posX < mapSizeX && temp.posY >= 0 && temp.posY < mapSizeY) {
			if (map[temp.posX][temp.posY] != null) {
				//System.out.println("you kill a enemy");
				count++;
				map[temp.posX][temp.posY] = null;
			}
		}
		return count;
	}
	
	// let a player move
	public void move(Player p, int operate) throws Exception {
		/*
		 * operate      1            front
		 *           4     2     left     right
		 *              3            back
		 */
		Player temp = new Player();
		temp.posX = p.posX;
		temp.posY = p.posY;
		temp.setDirection(p.getDirection());
		switch (operate) {
		case 1:
			temp.moveFront();
			break;
		case 2:
			temp.moveRight();
			break;
		case 3:
			temp.moveBack();
			break;
		case 4:
			temp.moveLeft();
			break;
		default:
			throw new Exception("move operate sign error");
		}
		if (temp.posX >= 0 && temp.posX < mapSizeX && temp.posY >= 0 && temp.posY < mapSizeY) {
			map[p.posX][p.posY] = null;
			p.posX = temp.posX;
			p.posY = temp.posY;
			p.setDirection(temp.getDirection());
			map[p.posX][p.posY] = p;
		} else {
			throw new Exception("move over wall");
		}
	}
	
	// let a player turn, operate is 1 mean left, 2 is right
	public void turn(Player p, int operate) throws Exception {
		if (operate == 1) {
			p.turnLeft();
		} else if (operate == 2) {
			p.turnRight();
		} else {
			throw new Exception("turn operate sign error");
		}
	}
	
	// reset all player position
	public void randomPlayer() {
		boolean[][] randomMap = new boolean[mapSizeX][mapSizeY];
		for (int x = 0; x < mapSizeX; x++) {
			for (int y = 0; y < mapSizeY; y++) {
				randomMap[x][y] = true;
			}
		}
		// random pick position form random map
		Random random = new Random();
		for (Player now : player) {
			try {
				now.setDirection(random.nextInt(8) + 1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			int x, y;
			do {
				x = random.nextInt(mapSizeX);
				y = random.nextInt(mapSizeY);
			} while (randomMap[x][y] != true);
			randomMap[x][y] = false;
			now.posX = x;
			now.posY = y;
			map[x][y] = now;
		}
	}

	// show the game in console
	public void printMap() {
		// print the map top
		for (int s = 0; s < mapSizeX + 2; s++) {
			System.out.print("--");
		}
		System.out.println();
		// print the map wall
		for (int y = mapSizeY - 1; y >= 0; y--) {
			System.out.print("|");
			for (int x = 0; x < mapSizeX; x++) {
				if (map[x][y] != null) {
					switch (map[x][y].getDirection()) {
					case 1:
						System.out.print("w");
						break;
					case 2:
						System.out.print("e");
						break;
					case 3:
						System.out.print("d");
						break;
					case 4:
						System.out.print("c");
						break;
					case 5:
						System.out.print("x");
						break;
					case 6:
						System.out.print("z");
						break;
					case 7:
						System.out.print("a");
						break;
					case 8:
						System.out.print("q");
						break;
					}
					//System.out.print(map[x][y].sign);
				} else {
					System.out.print(' ');
				}
			}
			System.out.print("|");
			System.out.print("|");
			for (int x = 0; x < mapSizeX; x++) {
				if (map[x][y] != null) {
					System.out.print(map[x][y].sign);
				} else {
					System.out.print(' ');
				}
			}
			System.out.println("|");
		}
		// print the floor
		for (int s = 0; s < mapSizeX + 2; s++) {
			System.out.print("--");
		}
		System.out.println();
	}
	
	@Override
	public Game clone() {
		Game res = null;
		try {
			res = (Game) super.clone();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// deep clone ArrayList and controlPlayer
		res.player = new ArrayList<Player>();
		for (Player p : player) {
			Player temp = p.clone();
			if (p == controlPlayer) {
				res.controlPlayer = temp;
			}
			res.player.add(temp);	
		}
		// deep clone map[][]
		res.map = map.clone();
		for (int s = 0; s < map.length; s++) {
			res.map[s] = map[s].clone();
		}
		// let map link to new player
		for (Player p : player) {
			res.map[p.posX][p.posY] = null;
		}
		for (Player p : res.player) {
			res.map[p.posX][p.posY] = p;
		}
		
		return res;
	}

}